package org.xbill.DNS;

import java.io.IOException;
import java.io.PrintStream;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

public class ZoneTransferIn
{
  private static final int AXFR = 6;
  private static final int END = 7;
  private static final int FIRSTDATA = 1;
  private static final int INITIALSOA = 0;
  private static final int IXFR_ADD = 5;
  private static final int IXFR_ADDSOA = 4;
  private static final int IXFR_DEL = 3;
  private static final int IXFR_DELSOA = 2;
  private SocketAddress address;
  private List axfr;
  private TCPClient client;
  private long current_serial;
  private int dclass;
  private long end_serial;
  private Record initialsoa;
  private List ixfr;
  private long ixfr_serial;
  private SocketAddress localAddress;
  private int qtype;
  private int rtype;
  private int state;
  private long timeout = 900000L;
  private TSIG tsig;
  private TSIG.StreamVerifier verifier;
  private boolean want_fallback;
  private Name zname;

  private ZoneTransferIn(Name paramName, int paramInt, long paramLong, boolean paramBoolean, SocketAddress paramSocketAddress, TSIG paramTSIG)
  {
    this.address = paramSocketAddress;
    this.tsig = paramTSIG;
    if (paramName.isAbsolute())
    {
      this.zname = paramName;
      this.qtype = paramInt;
      this.dclass = 1;
      this.ixfr_serial = paramLong;
      this.want_fallback = paramBoolean;
      this.state = 0;
      return;
    }
    try
    {
      this.zname = Name.concatenate(paramName, Name.root);
    }
    catch (NameTooLongException localNameTooLongException)
    {
      throw new IllegalArgumentException("ZoneTransferIn: name too long");
    }
  }

  private void closeConnection()
  {
    try
    {
      if (this.client != null)
        this.client.cleanup();
      return;
    }
    catch (IOException localIOException)
    {
    }
  }

  private void doxfr()
    throws IOException, ZoneTransferException
  {
    Message localMessage;
    Record[] arrayOfRecord;
    sendQuery();
    if (this.state == 7);
    while (true)
    {
      int j;
      while (true)
      {
        return;
        byte[] arrayOfByte = this.client.recv();
        localMessage = parseMessage(arrayOfByte);
        if ((localMessage.getHeader().getRcode() == 0) && (this.verifier != null))
        {
          localMessage.getTSIG();
          if (this.verifier.verify(localMessage, arrayOfByte) != 0)
            fail("TSIG failure");
        }
        arrayOfRecord = localMessage.getSectionArray(1);
        if (this.state != 0)
          break label183;
        j = localMessage.getRcode();
        if (j == 0)
          break label128;
        if ((this.qtype != 251) || (j != 4))
          break;
        fallback();
        doxfr();
      }
      fail(Rcode.string(j));
      label128: Record localRecord = localMessage.getQuestion();
      if ((localRecord != null) && (localRecord.getType() != this.qtype))
        fail("invalid question section");
      if ((arrayOfRecord.length != 0) || (this.qtype != 251))
        break;
      fallback();
      doxfr();
    }
    label183: int i = 0;
    while (true)
    {
      while (true)
      {
        do
          if (i < arrayOfRecord.length)
            break label225;
        while ((this.state != 7) || (this.verifier == null) || (localMessage.isVerified()));
        fail("last message must be signed");
      }
      label225: parseRR(arrayOfRecord[i]);
      ++i;
    }
  }

  private void fail(String paramString)
    throws ZoneTransferException
  {
    throw new ZoneTransferException(paramString);
  }

  private void fallback()
    throws ZoneTransferException
  {
    if (!(this.want_fallback))
      fail("server doesn't support IXFR");
    logxfr("falling back to AXFR");
    this.qtype = 252;
    this.state = 0;
  }

  private long getSOASerial(Record paramRecord)
  {
    return ((SOARecord)paramRecord).getSerial();
  }

  private void logxfr(String paramString)
  {
    if (Options.check("verbose"))
      System.out.println(this.zname + ": " + paramString);
  }

  public static ZoneTransferIn newAXFR(Name paramName, String paramString, int paramInt, TSIG paramTSIG)
    throws UnknownHostException
  {
    if (paramInt == 0)
      paramInt = 53;
    return newAXFR(paramName, new InetSocketAddress(paramString, paramInt), paramTSIG);
  }

  public static ZoneTransferIn newAXFR(Name paramName, String paramString, TSIG paramTSIG)
    throws UnknownHostException
  {
    return newAXFR(paramName, paramString, 0, paramTSIG);
  }

  public static ZoneTransferIn newAXFR(Name paramName, SocketAddress paramSocketAddress, TSIG paramTSIG)
  {
    return new ZoneTransferIn(paramName, 252, 0L, false, paramSocketAddress, paramTSIG);
  }

  public static ZoneTransferIn newIXFR(Name paramName, long paramLong, boolean paramBoolean, String paramString, int paramInt, TSIG paramTSIG)
    throws UnknownHostException
  {
    if (paramInt == 0)
      paramInt = 53;
    return newIXFR(paramName, paramLong, paramBoolean, new InetSocketAddress(paramString, paramInt), paramTSIG);
  }

  public static ZoneTransferIn newIXFR(Name paramName, long paramLong, boolean paramBoolean, String paramString, TSIG paramTSIG)
    throws UnknownHostException
  {
    return newIXFR(paramName, paramLong, paramBoolean, paramString, 0, paramTSIG);
  }

  public static ZoneTransferIn newIXFR(Name paramName, long paramLong, boolean paramBoolean, SocketAddress paramSocketAddress, TSIG paramTSIG)
  {
    return new ZoneTransferIn(paramName, 251, paramLong, paramBoolean, paramSocketAddress, paramTSIG);
  }

  private void openConnection()
    throws IOException
  {
    this.client = new TCPClient(System.currentTimeMillis() + this.timeout);
    if (this.localAddress != null)
      this.client.bind(this.localAddress);
    this.client.connect(this.address);
  }

  private Message parseMessage(byte[] paramArrayOfByte)
    throws WireParseException
  {
    Message localMessage;
    try
    {
      localMessage = new Message(paramArrayOfByte);
      return localMessage;
    }
    catch (IOException localIOException)
    {
      if (localIOException instanceof WireParseException)
        throw ((WireParseException)localIOException);
      throw new WireParseException("Error parsing message");
    }
  }

  private void parseRR(Record paramRecord)
    throws ZoneTransferException
  {
    int i = paramRecord.getType();
    switch (this.state)
    {
    default:
      fail("invalid state");
    case 0:
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
    case 6:
    case 7:
    }
    while (true)
    {
      while (true)
      {
        do
        {
          do
            while (true)
            {
              while (true)
              {
                long l;
                while (true)
                {
                  while (true)
                  {
                    while (true)
                    {
                      while (true)
                      {
                        while (true)
                        {
                          while (true)
                          {
                            while (true)
                            {
                              return;
                              if (i != 6)
                                fail("missing initial SOA");
                              this.initialsoa = paramRecord;
                              this.end_serial = getSOASerial(paramRecord);
                              if ((this.qtype != 251) || (this.end_serial > this.ixfr_serial))
                                break;
                              logxfr("up to date");
                              this.state = 7;
                            }
                            this.state = 1;
                          }
                          if ((this.qtype == 251) && (i == 6) && (getSOASerial(paramRecord) == this.ixfr_serial))
                          {
                            this.rtype = 251;
                            this.ixfr = new ArrayList();
                            logxfr("got incremental response");
                            this.state = 2;
                          }
                          while (true)
                          {
                            while (true)
                              parseRR(paramRecord);
                            this.rtype = 252;
                            this.axfr = new ArrayList();
                            this.axfr.add(this.initialsoa);
                            logxfr("got nonincremental response");
                            this.state = 6;
                          }
                          Delta localDelta2 = new Delta(null);
                          this.ixfr.add(localDelta2);
                          localDelta2.start = getSOASerial(paramRecord);
                          localDelta2.deletes.add(paramRecord);
                          this.state = 3;
                        }
                        if (i != 6)
                          break;
                        this.current_serial = getSOASerial(paramRecord);
                        this.state = 4;
                        parseRR(paramRecord);
                      }
                      ((Delta)this.ixfr.get(this.ixfr.size() - 1)).deletes.add(paramRecord);
                    }
                    Delta localDelta1 = (Delta)this.ixfr.get(this.ixfr.size() - 1);
                    localDelta1.end = getSOASerial(paramRecord);
                    localDelta1.adds.add(paramRecord);
                    this.state = 5;
                  }
                  if (i != 6)
                    break label500;
                  l = getSOASerial(paramRecord);
                  if (l != this.end_serial)
                    break;
                  this.state = 7;
                }
                if (l == this.current_serial)
                  break;
                fail("IXFR out of sync: expected serial " + this.current_serial + " , got " + l);
                label500: ((Delta)this.ixfr.get(this.ixfr.size() - 1)).adds.add(paramRecord);
              }
              this.state = 2;
              parseRR(paramRecord);
            }
          while ((i == 1) && (paramRecord.getDClass() != this.dclass));
          this.axfr.add(paramRecord);
        }
        while (i != 6);
        this.state = 7;
      }
      fail("extra data");
    }
  }

  private void sendQuery()
    throws IOException
  {
    Record localRecord = Record.newRecord(this.zname, this.qtype, this.dclass);
    Message localMessage = new Message();
    localMessage.getHeader().setOpcode(0);
    localMessage.addRecord(localRecord, 0);
    if (this.qtype == 251)
      localMessage.addRecord(new SOARecord(this.zname, this.dclass, 0L, Name.root, Name.root, this.ixfr_serial, 0L, 0L, 0L, 0L), 2);
    if (this.tsig != null)
    {
      this.tsig.apply(localMessage, null);
      this.verifier = new TSIG.StreamVerifier(this.tsig, localMessage.getTSIG());
    }
    byte[] arrayOfByte = localMessage.toWire(65535);
    this.client.send(arrayOfByte);
  }

  public List getAXFR()
  {
    return this.axfr;
  }

  public List getIXFR()
  {
    return this.ixfr;
  }

  public Name getName()
  {
    return this.zname;
  }

  public int getType()
  {
    return this.qtype;
  }

  public boolean isAXFR()
  {
    int i;
    if (this.rtype == 252)
      i = 1;
    while (true)
    {
      return i;
      i = 0;
    }
  }

  public boolean isCurrent()
  {
    int i;
    if ((this.axfr == null) && (this.ixfr == null))
      i = 1;
    while (true)
    {
      return i;
      i = 0;
    }
  }

  public boolean isIXFR()
  {
    int i;
    if (this.rtype == 251)
      i = 1;
    while (true)
    {
      return i;
      i = 0;
    }
  }

  public List run()
    throws IOException, ZoneTransferException
  {
    List localList;
    try
    {
      openConnection();
      return localList;
    }
    finally
    {
      while (true)
      {
        closeConnection();
        throw localObject;
        localList = this.ixfr;
      }
    }
  }

  public void setDClass(int paramInt)
  {
    DClass.check(paramInt);
    this.dclass = paramInt;
  }

  public void setLocalAddress(SocketAddress paramSocketAddress)
  {
    this.localAddress = paramSocketAddress;
  }

  public void setTimeout(int paramInt)
  {
    if (paramInt < 0)
      throw new IllegalArgumentException("timeout cannot be negative");
    this.timeout = (1000L * paramInt);
  }

  public static class Delta
  {
    public List adds = new ArrayList();
    public List deletes = new ArrayList();
    public long end;
    public long start;
  }
}